 package apof.services
{
	import apof.model.User;
	import apof.dao.DataAccessInterface;
	import apof.model.Account;
	import apof.model.*
	import apof.dao.*;
	import apof.util.ConfigureReader;
	import mx.rpc.xml.SimpleXMLEncoder;
	import mx.states.Transition;
	public class Services
	{
		public var user:User;
		public var filter:Object;
		public var dao:DataAccessInterface;
		public var ana:AnalysisService;
		public var b:BService;
		
		static public var services:Services=null;
		//public function Services(){}
		public function Services(callback:Function) {
			//TODO: 此处需完善用户登录验证逻辑。
			var uid:int=int(Number((ConfigureReader.cfg["uid"])));
			Dao.dp.init(function():void{
				user=Dao.dp.retrive(User,uid) as User;
				ana=new AnalysisService(user);
				b = new BService();
				callback();
			});
		}
		//For Account
		public function getAccountNameList():Array{

			var array:Array=Dao.dp.retriveAll(Account,"uid",user.uid);
			var nameArray:Array=new Array();
			for (var i:int=0;i<array.length;i++){
				nameArray.push((array[i] as Account).name);
			}
			return nameArray;
		}
		public function updateAccount(id:int,newName:String):void{
			var account:Account = Dao.dp.retrive(Account,id) as Account;
			account.name = newName;
			Dao.dp.update(account);
		}
		public function updateAccountAlpha(oldName:String,newName:String):void{
			var aid:int=findIdByName(Account,oldName,"aid");
			updateAccount(aid,newName);	
		}
		public function createAccount(newName:String):void{
			var account:Account = new Account;
			account.name = newName;
			account.balance = 0;
			account.uid=user.uid;
			Dao.dp.create(account);
		}
		public function removeAccount(account:Account):void{
			Dao.dp.removeById(Account,account.aid);	
		}
		public function removeAccountByName(name:String):void{
			var aid:int=findIdByName(Account,name,"aid");
			Dao.dp.removeById(Account,aid);	
		}
		//ForCategory
		private function generateXML(consume:ConsumeType):XML{
			var self:XML=
			<defaultNode 
				name={consume.name}
				id={consume.ctid}
				ediable={consume.editable}
				appendable={consume.appendable}
				cnstraint={consume.cnstraint} 
				parentCtid={consume.parentCtid}
				rank={consume.rank}
				isBranch="true"
			></defaultNode>;
			
			for each(var son:ConsumeType in consume.subTypes) {
				self.appendChild(generateXML(son));
			}
			return self;
		}
		public function getCategory():XML{
			var root:ConsumeType=Dao.dp.retrive(ConsumeType,0) as ConsumeType;
			var xml:XML=generateXML(root);
			return xml;
		}
		public function updateCategory(ID:int,attributeModified:Object):void{
			var ct:ConsumeType=Dao.dp.retrive(ConsumeType,ID) as ConsumeType;
			for (var key:String in attributeModified) {
				ct[key]=attributeModified[key];
			}

			Dao.dp.update(ct);
		}
		public function updateCategoryByName(oldName:String, newName:String):void{
			var arr:Array=Dao.dp.retriveAll(ConsumeType,null,null,function(item:ConsumeType,...rest):Boolean {
				return item.name==oldName;
			});
			if(arr.length==1) {
				var ct:ConsumeType=arr[0] as ConsumeType;
				ct.name=newName;
				Dao.dp.update(ct);				
			}
		}
		
		public function createNewCategory(name:String,parentCtid:int):void{
			var ct:ConsumeType=new ConsumeType();
			ct.name=name;
			ct.parentCtid=parentCtid;
			Dao.dp.create(ct);
		}
		public function createNewCategoryByParentName(name:String,parentName:String):void{
			var arr:Array=Dao.dp.retriveAll(ConsumeType,null,null,function(item:ConsumeType,...rest):Boolean {
				return item.name==parentName;
			});
			if(arr.length==1) {
				var parentId:int=(arr[0] as ConsumeType).ctid;
				 createNewCategory(name,parentId);
			}
		}
		
		public function removeCategory(ctid:int):void {
			Dao.dp.removeById(ConsumeType,ctid);	
		}
		public function removeCategoryByName(name:String):void{
			var arr:Array=Dao.dp.retriveAll(ConsumeType,null,null,function(item:ConsumeType,...rest):Boolean {
			var	 b:Boolean=(item.name==name);
				return b;
			});
			if(arr.length==1) {
				var ct:ConsumeType=arr[0] as ConsumeType;
				Dao.dp.remove(ct);			
			}
		}
		
		//forFilter
		public function createNewFilter(name:String,categoryID:int):void{
			var filter:Filter=new Filter();
			filter.uid=user.uid;
			filter.keyword=name;
			filter.targetCtid=categoryID;
			Dao.dp.create(filter);
		}
		
		/**
		 * 未测试！
		 * **/
		public function findCategoryByName(name:String):Array{
			//compute scores of each category
			var scores:Object={};
			Dao.dp.retriveAll(Filter).forEach(
				function(ft:Filter,...rest):void{
					if(ft.access(name)) {
						if(scores[ft.targetCtid]==null)
							scores[ft.targetCtid]=ft.hitRatio();
						else scores[ft.targetCtid]=1-(1-scores[ft.targetCtid])*(1-ft.hitRatio());
					}
				}
				);
			//find top3 high score
			var top3:Array=[-1,-1,-1];
			scores[-1]=-1;
			for(var ctid:Object in scores) {
				for(var i:int=0;i<top3.length;i++) {
					if(scores[ctid]>scores[top3[i]]){
						for(var j:int=2;j>i;j--) {
							top3[j]=top3[j-1];
						}
						top3[i]=ctid;
						break;
					}
				}
			}
			for(var k:int=0; k<3; k++){
				top3[k]=(top3[k]==-1?"":(Dao.dp.retrive(ConsumeType,top3[k]) as ConsumeType).name);
			}
			return top3;
		}
		//forTransection
		
		public function getTransaction(startDay:Date,endDay:Date,aname:Array,categoryName:String):Array{
			
			if(startDay==null) startDay=new Date(0);
			if(endDay==null) endDay=new Date();
			
			return Dao.dp.retriveAll(Transection,null,null,function(ts:Transection,...rest):Boolean {
				if(! (ts.time.time>=startDay.time && ts.time.time<endDay.time+86400000))
					return false;
				if(aname!=null && ! aname.some( function(name:String,...rest2):Boolean{return ts.account.name==name;}))
					return false;
				var ct:ConsumeType=Dao.dp.retrive(ConsumeType,ts.ctid) as ConsumeType;
				if(ct==null || ct.ctid==-1)return false;
				if(categoryName!=null && categoryName!=ct.name && ct.parent.name!=categoryName)
					return false;
				return true;
			},"absoluteTime").reverse();
		}
		public function getTransactionAlpha(startDay:Date,endDay:Date,aid:Array,ctid:Array):Array{
			return null;
		}
		public function createTransaction(transaction:Transection):void{
			Dao.dp.create(transaction);
			//做Fielt学习反馈
			filtersFeedback(transaction);
			SynService.tc.commit(transaction.toString());
		}
		
		private function findIdByName(clazz:Class,name:String,idName:String):int {
			var arr:Array=Dao.dp.retriveAll(clazz,null,null,function(item:ApofModel,...rest):Boolean {
				return (item as Object).name==name;
			});
			if(arr.length==1) {
				return arr[0][idName];			
			}
			else 
				throw "unspecified name!";
		}
		
		public function createTransactionInDetail(transactionName:String,time:Date,description:String,
		shared:Boolean,accountName:String,categoryName:String, amount:int):void{
			var aid:int=findIdByName(Account,accountName,"aid");
			var ctid:int=findIdByName(ConsumeType,categoryName,"ctid");
			var tr:Transection=new Transection();
			tr.name=transactionName;
			tr.time=time;
			tr.description=description;
			tr.shared=shared;
			tr.aid=aid;
			tr.ctid=ctid;
			tr.amount = amount;
			Dao.dp.create(tr);
			SynService.tc.commit(tr.toString());
		}
		
		private function filtersFeedback(transaction:Transection):void {
			if(transaction.ctid==-1)return;
			Dao.dp.retriveAll(Filter).forEach(
				function(ft:Filter,...rest):void {
					if (ft.feedback(transaction.name,transaction.ctid))
					Dao.dp.update(ft);
				}
				);
		}
		
		public function updateTransaction(tid:int,transactionName:String,time:Date,description:String,
		shared:Boolean,accountName:String,categoryName:String, amount:int):void{
			var aid:int=findIdByName(Account,accountName,"aid");
			var ctid:int=findIdByName(ConsumeType,categoryName,"ctid");
			var tr:Transection=Dao.dp.retrive(Transection,tid) as Transection;
			tr.name=transactionName;
			tr.time=time;
			tr.description=description;
			tr.shared=shared;
			tr.aid=aid;
			tr.ctid=ctid;
			tr.amount = amount;
			Dao.dp.update(tr);
		}
		public function removeTransaction(tid:int):void{
			Dao.dp.removeById(Transection,tid);
		}
		public static function init(callback:Function):void {
			services=new Services(callback);
		}
		public static function getInstance():Services{
//			if (Services.services==null){
//				Services.services=new Services();
//				return services;
//			}else{
				return services;
//			}
		}
		public function setUser(user:User):void{
			this.user=user;
		}
	}
}